#include "myLog.h"
#include <unistd.h>
#include <stdio.h>
#include "zeroCopyPublisher.hpp"
#include "zeroCopySubscriber.hpp"

#include "reliabilitySubscriber.hpp"
#include "reliabilityPublisher.hpp"
#include <experimental/filesystem>
#include <fastrtps/Domain.h>

using namespace std;
using namespace eprosima;
using namespace fastrtps;
using namespace rtps;

string executionPath()
{
#if defined(__linux)
    static char elfPath[4096];
    auto size = readlink("/proc/self/exe",elfPath,sizeof(elfPath));
    if(size <= 0 || size > (decltype(size))(sizeof(elfPath) - 1))
    {
        fprintf(stderr, "get execution path failed. %s %d\n",__FILE__,__LINE__);
        return "";
    }
    for(int i = size - 1;i >= 0;--i)
    {
        if('/' == elfPath[i])
        {
            elfPath[1 + i] = 0;
            return string(elfPath);
        }
        if(0 == i)
        {
            fprintf(stderr, "get execution path failed. %s %d\n",__FILE__,__LINE__);
            return "";
        }
    }
#endif
    return "";
}


static bool isLogConfFileValid(const string &path) 
{
    if (true == path.empty()) 
    {
        fprintf(stderr, "invalid log configuration file, detail: file path is nil.\n");
        return false;
    }

    if (false == experimental::filesystem::exists(path)) 
    {
        fprintf(stderr, "invalid log configuration file, detail: file '%s' is not exists.\n", path.c_str());
        return false;
    }

    if (false == experimental::filesystem::is_regular_file(path)) 
    {
        fprintf(stderr, "invalid log configuration file, detail: file '%s' is not a regular file.\n", path.c_str());
        return false;
    }

    return true;
}


static void reliabilityInit(int argc, char **argv)
{
    int type = 1;
    int transport = 1;
    bool paramsOk = true;
	std::string topic = "BenchMarkTopic";
	int domain = 0;
	int size = 0;
	int test_time = 10000;
	int tick_time = 100;
	int wait_time = 1000;
	ReliabilityQosPolicyKind kind = RELIABLE_RELIABILITY_QOS;
    if(argc >= 2)
    {
        if(strcmp(argv[2],"publisher")==0)
        {
            type = 1;
        }
        else if(strcmp(argv[2],"subscriber")==0)
        {
            type = 2;
        }
        else
        {
            paramsOk = false;
        }

        if (paramsOk && argc > 2)
        {
            if (strcmp(argv[3], "udp") == 0)
            {
                transport = 1;
            }
            else if (strcmp(argv[3], "tcp") == 0)
            {
                transport = 2;
            }
            else if (strcmp(argv[3], "udp6") == 0)
            {
                transport = 3;
            }
            else if (strcmp(argv[3], "tcp6") == 0)
            {
                transport = 4;
            }
            else
            {
                paramsOk = false;
            }
        }
        if (argc > 4)
        {
            if (argc % 2 != 0)
            {
                paramsOk = false;
            }
            for (int i = 4; i < argc; i += 2)
            {
                if (strcmp(argv[i], "-topic") == 0)
                {
                    topic = argv[i + 1];
                }
                else if (strcmp(argv[i], "-domain") == 0)
                {
                    domain = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-time") == 0)
                {
                    test_time = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-size") == 0)
                {
                    if (strcmp(argv[i + 1], "none") == 0)
                    {
                        size = 0;
                    }
                    else if (strcmp(argv[i + 1], "small") == 0)
                    {
                        size = 1;
                    }
                    else if (strcmp(argv[i + 1], "medium") == 0)
                    {
                        size = 2;
                    }
                    else if (strcmp(argv[i + 1], "big") == 0)
                    {
                        size = 3;
                    }
                    else
                    {
                        paramsOk = false;
                    }
                }
                else if (strcmp(argv[i], "-reliable") == 0)
                {
                    if (strcmp(argv[i + 1], "true") == 0)
                    {
                        //它表明该服务将尝试发送数据写入器历史缓存中的所有样本，期望来自数据阅读器的到达确认。（该服务将重新传输丢失的数据样本）
                        kind = RELIABLE_RELIABILITY_QOS;
                    }
                    else if (strcmp(argv[i + 1], "false") == 0)
                    {
                        //表示可以不重新发送丢失的样本，所以不等待到达确认即可发送消息。
                        kind = BEST_EFFORT_RELIABILITY_QOS;
                    }
                    else
                    {
                        paramsOk = false;
                    }
                }
                else if (strcmp(argv[i], "-tick") == 0)
                {
                    tick_time = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-wait") == 0)
                {
                    wait_time = atoi(argv[i + 1]);
                }
                else
                {
                    paramsOk = false;
                }
            }
        }
    }
    else
    {
        paramsOk = false;
    }

    if (!paramsOk)
    {
        qlog_i("publisher OR subscriber argument needed");
        qlog_i("tcp OR udp argument needed");
        qlog_i("-------------------------------------------------------------------- ");
        qlog_i("Common optional arguments: [-topic] [-domain][-reliable] ");
        qlog_i("\t-topic: Prefix topic for the communication ");
        qlog_i("\t-domain: Domain id ");
        qlog_i("\t-reliable: Sets if the communication is going to be Reliable or Best_Effort. Values [true] [false] ");
        qlog_i("\t-size: Size of the message. Values [none] [small] [medium] [big] ");
        qlog_i("\t\t: none. The message only contains an int value");
        qlog_i("\t\t: small. The message contains an int value and an array of 16Kb ");
        qlog_i("\t\t: medium. The message contains an int value and an array of 512Kb");
        qlog_i("\t\t: big. The message contains an int value and an array of 8Mb");
        qlog_i("-------------------------------------------------------------------- ");
        qlog_i("publisher has an optional argument: [time] ");
        qlog_i("\t-wait: Milliseconds before starting the sampling. ");
        qlog_i("\t-time: Milliseconds that the test is going to run. ");
        qlog_i("\t-tick: Milliseconds to take samples of the performance. ");
        qlog_i("-------------------------------------------------------------------- ");
    }

    switch(type)
    {
        case 1:
            {
                reliabilityPub* mypub = new reliabilityPub();
                if(mypub->init(transport, kind, test_time, tick_time, wait_time, topic, domain, size))
                {
                    mypub->run();
                }
                delete mypub;
                break;
            }
        case 2:
            {
                reliabilitySub* mysub = new reliabilitySub();
                if(mysub->init(transport, kind, topic, domain, size))
                {
                    mysub->run();
                }
                delete mysub;
                break;
            }
    }
    Domain::stopAll();
}

static void zeroCopyInit(int argc, char **argv)
{
    int type = 1;
    int transport = 1;
    bool paramsOk = true;
	std::string topic = "zeroCopyTopic";
	int domain = 0;
	int size = 0;
	int data_size = 1000;//1000M
	int tick_time = 0;
	int wait_time = 1000;
    int loan_flag = 1;
	ReliabilityQosPolicyKind kind = RELIABLE_RELIABILITY_QOS;
    if(argc >= 2)
    {
        if(strcmp(argv[2],"publisher")==0)
        {
            type = 1;
        }
        else if(strcmp(argv[2],"subscriber")==0)
        {
            type = 2;
        }
        else
        {
            paramsOk = false;
        }

        if (paramsOk && argc > 2)
        {
            if (strcmp(argv[3], "udp") == 0)
            {
                transport = 1;
            }
            else if (strcmp(argv[3], "tcp") == 0)
            {
                transport = 2;
            }
            else if (strcmp(argv[3], "udp6") == 0)
            {
                transport = 3;
            }
            else if (strcmp(argv[3], "tcp6") == 0)
            {
                transport = 4;
            }
            else
            {
                paramsOk = false;
            }
        }
        if (argc > 4)
        {
            if (argc % 2 != 0)
            {
                paramsOk = false;
            }
            for (int i = 4; i < argc; i += 2)
            {
                if (strcmp(argv[i], "-topic") == 0)
                {
                    topic = argv[i + 1];
                }
                else if (strcmp(argv[i], "-domain") == 0)
                {
                    domain = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-mb") == 0)
                {
                    data_size = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-size") == 0)
                {
                    if (strcmp(argv[i + 1], "none") == 0)
                    {
                        size = 0;
                    }
                    else if (strcmp(argv[i + 1], "small") == 0)
                    {
                        size = 1;
                    }
                    else if (strcmp(argv[i + 1], "medium") == 0)
                    {
                        size = 2;
                    }
                    else if (strcmp(argv[i + 1], "big") == 0)
                    {
                        size = 3;
                    }
                    else
                    {
                        paramsOk = false;
                    }
                }
                else if (strcmp(argv[i], "-reliable") == 0)
                {
                    if (strcmp(argv[i + 1], "true") == 0)
                    {
                        //它表明该服务将尝试发送数据写入器历史缓存中的所有样本，期望来自数据阅读器的到达确认。（该服务将重新传输丢失的数据样本）
                        kind = RELIABLE_RELIABILITY_QOS;
                    }
                    else if (strcmp(argv[i + 1], "false") == 0)
                    {
                        //表示可以不重新发送丢失的样本，所以不等待到达确认即可发送消息。
                        kind = BEST_EFFORT_RELIABILITY_QOS;
                    }
                    else
                    {
                        paramsOk = false;
                    }
                }
                else if (strcmp(argv[i], "-tick") == 0)
                {
                    tick_time = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-wait") == 0)
                {
                    wait_time = atoi(argv[i + 1]);
                }
                else if (strcmp(argv[i], "-loan") == 0) 
                {
                    if (strcmp(argv[i + 1], "true") == 0)
                    {
                        loan_flag = 1;
                    }
                    else if (strcmp(argv[i + 1], "false") == 0)
                    {
                        loan_flag = 0;
                    }
                    else
                    {
                        paramsOk = false;
                    }
                }
                else
                {
                    paramsOk = false;
                }
            }
        }
    }
    else
    {
        paramsOk = false;
    }

    if (!paramsOk)
    {
        qlog_i("publisher OR subscriber argument needed");
        qlog_i("tcp OR udp argument needed");
        qlog_i("-------------------------------------------------------------------- ");
        qlog_i("Common optional arguments: [-topic] [-domain][-reliable] ");
        qlog_i("\t-topic: Prefix topic for the communication ");
        qlog_i("\t-domain: Domain id ");
        qlog_i("\t-reliable: Sets if the communication is going to be Reliable or Best_Effort. Values [true] [false] ");
        qlog_i("\t-size: Size of the message. Values [none] [small] [medium] [big] ");
        qlog_i("\t\t: none. The message only contains an int value");
        qlog_i("\t\t: small. The message contains an int value and an array of 10Kb ");
        qlog_i("\t\t: medium. The message contains an int value and an array of 1Mb");
        qlog_i("\t\t: big. The message contains an int value and an array of 10Mb");
        qlog_i("-------------------------------------------------------------------- ");
        qlog_i("publisher has an optional argument: [time] ");
        qlog_i("\t-mb: The Mb amount of data to be transferred. ");
        qlog_i("\t-loan: The shared memory was enabled. ");
        qlog_i("\t-tick: Milliseconds to take samples of the performance. ");
        qlog_i("-------------------------------------------------------------------- ");
    }

    switch(type)
    {
        case 1:
            {
                zeroCopyPublisher* mypub = new zeroCopyPublisher();
                if(mypub->init(transport, kind, data_size, tick_time, topic, domain, loan_flag, size))
                {
                    mypub->run();
                }
                delete mypub;
                break;
            }
        case 2:
            {
                zeroCopySubscriber* mysub = new zeroCopySubscriber();
                if(mysub->init(transport, kind, topic, domain, loan_flag, size))
                {
                    mysub->run();
                }
                delete mysub;
                break;
            }
    }
    Domain::stopAll();
}

int main(int argc, char **argv) 
{

    
    std::cout << "benchMark. Test starts..." << std::endl;
    auto err = 0;
    auto exePath = executionPath();
    if (true == exePath.empty()) 
    {
        return err;
    }
    string logConfFileVec[] = {
        exePath + "mylog.conf",
        exePath + "conf/mylog.conf",
    };
    bool ret = false;
    for (const auto& item: logConfFileVec) 
    {
        ret |= isLogConfFileValid(item);
        if (true == ret) 
        {
            ret = my_logInit(item.c_str());
            if(false == ret) 
            {
                fprintf(stderr, "init log with configuration file '%s' failed.\n", item.c_str());
            }
            else 
            {
                qlog_i("logger init done('%s').", item.c_str());
                break;
            }
        }
    }
    if (false == ret) 
    {
        fprintf(stderr, "logger init failed.\n");
        return err;
    }
    if(strcmp(argv[1],"shm")==0) {
        zeroCopyInit(argc,argv);
    } else if(strcmp(argv[1],"mark")==0) {
        reliabilityInit(argc,argv);
    } else {
        qlog_e("shm OR mark argument needed");
    }
    
    

    return 0;
}

